package com.ruoyi.web.controller.common;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.config.props.AllPathProperties;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.CalcThreadManager;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.SseEmitterUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.biz.CommonUtil;
import com.ruoyi.common.utils.biz.ResultSaveModule;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.poi.PoiWordUtil;
import com.ruoyi.framework.config.ServerConfig;
import com.ruoyi.utils.DbServiceUtil;
import com.ruoyi.web.server.entity.ResultSave;
import com.ruoyi.web.server.service.ResultSaveService;
import com.ruoyi.web.vo.DifferentSpaceVo;
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * <p>
 * </p>
 *
 * @author liuhl
 * @since 2024-06-25
 */

@RestController
@RequestMapping("/common")
@Api(tags = "公共方法")
public class CommonController
{
    private static final Logger log = LoggerFactory.getLogger(CommonController.class);

    @Autowired
    private ServerConfig serverConfig;

    @Autowired
    private AllPathProperties allPath;

    @Autowired
    private ResultSaveService resultSaveService;

    private static final String FILE_DELIMETER = ",";

    /**
     * 公共接口
     * 关闭计算线程
     */
    @GetMapping("/cancel")
    @ApiOperation("关闭计算线程")
    public R cancel(@ApiParam(value= "sseid") @RequestParam String sseid) {
        String key = sseid+ SecurityUtils.getLoginUser().getUsername();
        //关闭计算线程
        CalcThreadManager.cancelProcess(key);
        //关闭sse
        SseEmitterUtils.remove(key);
        return R.ok();
    }


    @GetMapping("/clientIp")
    public AjaxResult clientIp(HttpServletRequest request){
        String ipAddr = IpUtils.getIpAddr(request);
        return AjaxResult.success(ipAddr);
    }

    /**
     * 通用下载请求
     * 
     * @param fileName 文件名称
     * @param delete 是否删除
     */
    @GetMapping("/download")
    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
    {
        try
        {
            if (!FileUtils.checkAllowDownload(fileName))
            {
                throw new Exception(StringUtils.format("文件名称({})非法，不允许下载。 ", fileName));
            }
            String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
            String filePath = RuoYiConfig.getDownloadPath() + fileName;

            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            FileUtils.setAttachmentResponseHeader(response, realFileName);
            FileUtils.writeBytes(filePath, response.getOutputStream());
            if (delete)
            {
                FileUtils.deleteFile(filePath);
            }
        }
        catch (Exception e)
        {
            log.error("下载文件失败", e);
        }
    }

    /**
     * 通用上传请求（单个）
     */
    @PostMapping("/upload")
    public AjaxResult uploadFile(MultipartFile file) throws Exception
    {
        try
        {
            // 上传文件路径
            String filePath = RuoYiConfig.getUploadPath();
            // 上传并返回新文件名称
            String fileName = FileUploadUtils.upload(filePath, file);
            String url = serverConfig.getUrl() + fileName;
            AjaxResult ajax = AjaxResult.success();
            ajax.put("url", url);
            ajax.put("fileName", fileName);
            ajax.put("newFileName", FileUtils.getName(fileName));
            ajax.put("originalFilename", file.getOriginalFilename());
            return ajax;
        }
        catch (Exception e)
        {
            return AjaxResult.error(e.getMessage());
        }
    }

    /**
     * 通用上传请求（多个）
     */
    @PostMapping("/uploads")
    public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception
    {
        try
        {
            // 上传文件路径
            String filePath = RuoYiConfig.getUploadPath();
            List<String> urls = new ArrayList<String>();
            List<String> fileNames = new ArrayList<String>();
            List<String> newFileNames = new ArrayList<String>();
            List<String> originalFilenames = new ArrayList<String>();
            for (MultipartFile file : files)
            {
                // 上传并返回新文件名称
                String fileName = FileUploadUtils.upload(filePath, file);
                String url = serverConfig.getUrl() + fileName;
                urls.add(url);
                fileNames.add(fileName);
                newFileNames.add(FileUtils.getName(fileName));
                originalFilenames.add(file.getOriginalFilename());
            }
            AjaxResult ajax = AjaxResult.success();
            ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
            ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
            ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
            ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
            return ajax;
        }
        catch (Exception e)
        {
            return AjaxResult.error(e.getMessage());
        }
    }

    /**
     * 本地资源通用下载
     */
    @GetMapping("/download/resource")
    public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
            throws Exception
    {
        try
        {
            if (!FileUtils.checkAllowDownload(resource))
            {
                throw new Exception(StringUtils.format("资源文件({})非法，不允许下载。 ", resource));
            }
            // 本地资源路径
            String localPath = RuoYiConfig.getProfile();
            // 数据库资源地址
            String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
            // 下载名称
            String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            FileUtils.setAttachmentResponseHeader(response, downloadName);
            FileUtils.writeBytes(downloadPath, response.getOutputStream());
        }
        catch (Exception e)
        {
            log.error("下载文件失败", e);
        }
    }

    /**
     * 通用结果保存
     */
    @ApiOperation("通用-结果入库保存")
    @PostMapping("/result/save")
    @ApiImplicitParams(
        {
        @ApiImplicitParam(name ="module",value="大模块",type = "String",required = true,paramType="body",dataTypeClass = String.class),
        @ApiImplicitParam(name ="submodule",value="大模块中的小模块",type = "String",required = true,paramType="body",dataTypeClass = String.class),
        @ApiImplicitParam(name ="resultData",value="结果数据文件路径,数组形式的字符串",type = "String",required = true,paramType="body",dataTypeClass = String.class),
        @ApiImplicitParam(name ="startTime",value="起始时间(不是必传)",type = "String",required = false,paramType="body",dataTypeClass = String.class),
        @ApiImplicitParam(name ="endTime",value="结束时间(不是必传)",type = "String",required = false,paramType="body",dataTypeClass = String.class),
        @ApiImplicitParam(name ="其他字段",value="其他字段是什么就传什么",type = "String",required = true,paramType="body",dataTypeClass = String.class)
        }
    )
    public AjaxResult resultSave(@RequestBody Map<String,Object> paramMap) {
         return resultSaveService.resultSave(paramMap);
    }


    @PostMapping("/res/download")
    @ApiOperation(value = "通用的每个页面的结果下载", notes = "通用的每个页面的结果下载", httpMethod = "POST")
    public void downloadBatch(@RequestBody Map<String,Object> paramMap, HttpServletResponse response) throws IOException {

        String resultData = paramMap.get("resultData").toString();
        String submodule = paramMap.get("submodule").toString();
        String submoduleName = ResultSaveModule.getSubmoduleName(submodule);
        //每个文件的路径
        List<String> resultDataList = JSONUtil.parseArray(resultData)
                .toList(String.class)
                .stream()
                .map(String::trim)
                .filter(StrUtil::isNotBlank)
                .distinct()
                .collect(Collectors.toList());

        //每个文件的名字仍然不变化
        String[] fileNames = resultDataList.stream().map(filePath -> {
            return filePath.substring(filePath.lastIndexOf("/") + 1);
        }).toArray(String[]::new);

        //每个文件的输入流
        InputStream[] isArray = null;
        try {
            isArray = resultDataList.stream().map(name -> {
                File file = new File(name);
                if (!file.exists()) {
                    log.info("path:{} 指向的文件不存在", name);
                }
                try {
                    return new FileInputStream(file);
                } catch (FileNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }).toArray(InputStream[]::new);
            // 压缩至输出流
            //文件下载
            String zipAllName = submoduleName + "_" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_FORMAT) + ".zip";
            FileUtils.setAttachmentResponseHeader(response, zipAllName);
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            OutputStream os = response.getOutputStream();
            ZipUtil.zip(os, fileNames, isArray);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        } finally {
            if (response.getOutputStream() != null) {
                response.getOutputStream().close();
            }
            if (isArray != null) {
                Arrays.asList(isArray).forEach(is -> {
                    try {
                        is.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                });
            }
        }
    }


    /**
     * 通用结果保存
     */
    @ApiOperation("通用-报告下载")
    @PostMapping("/report/download")
    public void reportDownload(@RequestBody Map<String,Object> paramMap, HttpServletResponse response) throws Exception {
        String submodule = paramMap.get("submodule").toString();
        String fileName = null;
        String filePath = null;
        //对于模板文件的修改分为三部分：字符串替换，图片插入，表格渲染
        Map<String, Object> wordParams = new HashMap<>(0);
        boolean isPicAppend = false;
        String delimiter = "。";
        if (ResultSaveModule.MODEL_ASSESS_ALT.equalsIgnoreCase(submodule)){
            filePath = "word/不同高度条件下大气模型适用性评估报告.docx";

            //1.字符串替换
            String satellite = paramMap.get("satellite").toString();
            List<String> altNameList = Arrays.asList(satellite.split(","))
                    .stream()
                    .map(o -> o.split(":")[0].replace("morethan", "以上") + "km")
                    .collect(Collectors.toList());

            String altRangeAllStr = IntStream.range(0, altNameList.size()).mapToObj(i -> {
                return "高度范围" + (i+1) + "：" + altNameList.get(i);
            }).collect(Collectors.joining(delimiter));

            String altRangeStr = String.join("、", altNameList);

            // duration = 2009年04月01日-2009年04月30日
            String startTime = getChineseDateStr(paramMap.get("startTime").toString());
            String endTime = getChineseDateStr(paramMap.get("endTime").toString());
            String duration = startTime + " 至 " + endTime;

            // altMin 和 altMax
            String[] altitudeArr = paramMap.get("altitude").toString().split(",");

            // interval = 1440
            String interval = paramMap.get("interval").toString();
            // models = JB2008模型、NRLMSISE-00模型、DTM2020模型、Jacchia71模型
            String models = ((List<String>) paramMap.get("model")).stream().map(o->(o.toUpperCase())+"模型").collect(Collectors.joining("、"));

            // bestModel = 高度范围在0km~472km最优模型为：nrlmsise00；高度超过472km最优模型为：JB2008。
            List<String> bestModel = (List<String>)paramMap.get("bestModel");
            String bestModelStr = bestModel.stream().collect(Collectors.joining(delimiter));

            wordParams = MapUtil.builder(new HashMap<String, Object>())
                    .put("altMin", altitudeArr[0])
                    .put("altMax", altitudeArr[1])
                    .put("duration", duration)
                    .put("interval", interval)
                    .put("altRangeAllStr", altRangeAllStr)
                    .put("altRangeStr", altRangeStr)
                    .put("models", models)
                    .put("bestModel", bestModelStr)
                    .build();

            fileName = "不同高度条件下大气模型适用性评估报告.docx";

            isPicAppend = true;

        } else if (ResultSaveModule.MODEL_ASSESS_DIF_SPACE.equalsIgnoreCase(submodule)){

            filePath = "word/不同空间环境条件下大气模型适用性评估报告.docx";


            String[] solarRange = paramMap.get("solarRange").toString().split(",");
            String[] magRange = paramMap.get("magRange").toString().split(",");
            String year = paramMap.get("year").toString();
            //periods_of_severe_magnetic_storms_time
            String magLevelName = DifferentSpaceVo.getMagLevelName(paramMap.get("magLevel").toString());
            String solarLevelName = DifferentSpaceVo.getSolarLevelName(paramMap.get("solarLevel").toString());
            String assessTime = getChineseDateStr(paramMap.get("assessTime").toString());
            String assessModelName = ((List<String>)paramMap.get("model"))
                    .stream()
                    .map(o -> (o.toUpperCase())+"模型")
                    .collect(Collectors.joining("、"));

            // altMin 和 altMax
            String[] altitudeArr = paramMap.get("altitude").toString().split(",");
            // interval = 1440
            String interval = paramMap.get("interval").toString();
            String satellite = paramMap.get("satellite").toString();
            List<String> altNameList = Arrays.asList(satellite.split(","))
                    .stream()
                    .map(o -> o.split(":")[0].replace("morethan", "以上") + "km")
                    .collect(Collectors.toList());

            String altRangeAllStr = IntStream.range(0, altNameList.size()).mapToObj(i -> {
                return "高度范围" + (i+1) + "：" + altNameList.get(i);
            }).collect(Collectors.joining(delimiter));

            String altRangeStr = String.join("、", altNameList);

            List<String> bestModel = (List<String>)paramMap.get("bestModel");
            String bestModelStr = bestModel.stream().collect(Collectors.joining(delimiter));

            wordParams = MapUtil.builder(new HashMap<String, Object>())
                    .put("solarMin", solarRange[0])
                    .put("solarMax", solarRange[1])
                    .put("magMin", magRange[0])
                    .put("magMax", magRange[1])
                    .put("year", year)
                    .put("magLevelName", magLevelName)
                    .put("solarLevelName", solarLevelName)
                    .put("assessTime", assessTime)
                    .put("assessModelName", assessModelName)
                    .put("bestModelStr", bestModelStr)
                    .put("altMin", altitudeArr[0])
                    .put("altMax", altitudeArr[1])
                    .put("interval", interval)
                    .put("altRangeAllStr", altRangeAllStr)
                    .put("altRangeStr", altRangeStr)
                    .build();

            fileName = "不同空间环境条件下大气模型适用性评估报告.docx";

            isPicAppend = true;

        } else if (ResultSaveModule.MODEL_ASSESS_SPACE.equalsIgnoreCase(submodule)) {

            filePath = "word/空间环境指数适用性评估报告.docx";


            String indexName = paramMap.get("indexType").toString();
            //第1天至2天
            String[] dayArr = paramMap.get("whichDay").toString().split(",");
            String whichDay = "第"+dayArr[0]+"天至第"+dayArr[1]+"天";

            List<String> forsecastMethod = (List<String>)paramMap.get("forsecastMethod");
            String methodName = forsecastMethod.stream().map(CommonController::getMethodName).collect(Collectors.joining("、"));

            String startTime = getChineseDateStr(paramMap.get("startTime").toString());
            String endTime = getChineseDateStr(paramMap.get("endTime").toString());
            String timeRange = startTime + " 至 " + endTime;

            wordParams = MapUtil.builder(new HashMap<String, Object>())
                    .put("indexName", indexName)
                    .put("whichDay", whichDay)
                    .put("methodName", methodName)
                    .put("timeRange", timeRange)
                    .build();

            fileName = "空间环境指数适用性评估报告.docx";

            isPicAppend = true;

        }

        if (StrUtil.isBlank(fileName)){
            throw new RuntimeException("未知的模块:"+submodule);
        }
        //2.图片插入, 图片的地址在txt(内容是json格式)中
        String txtPath = JSONUtil.parseArray(paramMap.get("resultData").toString()).get(0).toString();
        String content = CommonUtil.readStrData(new File(txtPath), null);
        Map<String, JSONObject> contentMap = JSONUtil.toBean(content,Map.class);
        Map<String,Object> jpgPathMap = JSONUtil.toBean(contentMap.get("jpgPath"), Map.class);
        wordParams.putAll(jpgPathMap);

        //3.表格渲染
        List<Map<String,Object>> tableData = (List<Map<String,Object>>) paramMap.get("tableData");
        wordParams.put("tableData",tableData);

        PoiWordUtil.writeTemplateWordToResp(filePath,fileName, wordParams, response, isPicAppend);

    }

    private static String getMethodName(String method){
        String name = "";
        switch (method) {
            case "54AR":
                name = "54阶自回归模型";
                break;
            case "LSTM":
                name = "长短时记忆神经网络模型";
                break;
            case "MULTILINER":
                name = "多元线性回归模型";
                break;
        }
        return name;
    }

    private static String getChineseDateStr(String dateStr){
        String[] split = dateStr.split("-");
        return split[0] + "年" + split[1] + "月" + split[2] + "日";
    }







}
